/*
 *  BufferSpace.h
 *  PX
 *
 *  Created by Patrick Zulian on 12/22/10.
 *  
 *
 */

#ifndef NSE_SOLVER_COMMUNICATOR_H
#define NSE_SOLVER_COMMUNICATOR_H

#include "Buffer.h"


namespace nsesolver {

	/**
	 * \brief This class manages space allocation in order to give trasparency to the buffer management and optmized re-allocation
	 */
	template< typename T >
	class BufferSpace /* Uncopiable */{
	private:
		int numberOfBuffers, minSize;
		Buffer< T > ** buffers;
		
		/**
		 * @param neededQuantity the number of needed buffers
		 * @return if there are enough allocated buffers
		 */
		inline 
		bool hasEnoughBuffers(const int neededQuantity)
		{
			return numberOfBuffers >= neededQuantity;
		}
		
		/**
		 * @param bufferNumber the buffer for which the size is tested
		 * @return true if the buffer has enough space
		 */
		inline 
		bool bufferHasSpace(const int bufferNumber, const int neededSize)
		{
            NSE_SOLVER_ASSERT( buffers != NULL );
            NSE_SOLVER_ASSERT( numberOfBuffers > 0);
            NSE_SOLVER_ASSERT( bufferNumber < numberOfBuffers );
            NSE_SOLVER_ASSERT( bufferNumber >= 0 );
            NSE_SOLVER_ASSERT( buffers[bufferNumber] != NULL );
			return buffers[bufferNumber]->size() > neededSize;
		}
		/**
		 * @param numberOfBuffers the number of buffer needed
		 * @param size the minimum size needed by all buffers
		 * @return true if needs resizing
		 */
		inline
		bool needsResizing(const int numberOfBuffers, const int size)
		{
			if ( !hasEnoughBuffers(numberOfBuffers) || size > this->minSize ) {
				return true;
			}
			
			return false;
		}
		
		/**
		 * resets all the states (to be used only if no buffer is allocated)
		 */
		inline
		void reset()
		{
			buffers = NULL;
			numberOfBuffers = minSize = 0;
		}
		
		
		/**
		 * @return if the buffers are not equal to NULL
		 */ 
		inline
		bool allocated()
		{
			return buffers != NULL;
		}
		
		/** To remove the possbility to copy an object of this class */
		BufferSpace(const BufferSpace &other);
		BufferSpace & operator=(const BufferSpace &other);
	public:
		BufferSpace()
		: numberOfBuffers(0), minSize(0), buffers(NULL)
		{
            NSE_SOLVER_ASSERT( false );
		}
		
		/**
		 * Instantiate a BufferSpace Object and allocates the buffer space
		 * @param numberOfBuffers 
		 */
		explicit BufferSpace(const int numberOfBuffers)
		: numberOfBuffers(numberOfBuffers), minSize(0), buffers(NULL)
		{
			allocate(numberOfBuffers, 1);
		}
		
		/**
		 * Instantiate a BufferSpace Object and allocates the buffer space and their size
		 * @param numberOfBuffers 
		 * @param minSize the size that every buffer will have
		 */
		BufferSpace(const int numberOfBuffers, const int minSize)
		: numberOfBuffers(numberOfBuffers), minSize(0), buffers(NULL)
		{
			allocate(numberOfBuffers, minSize);
		}
		
		/**
		 * @param bufferNumber the buffer that has to be reallocated
		 * @param size the length that the buffer has to have
		 */
		void reAllocateBuffer(const int bufferNumber, const int size)
		{
			NSE_SOLVER_ASSERT(bufferNumber >= 0 && bufferNumber < numberOfBuffers);
			//this method cannot be used unless buffers are already allocated
			NSE_SOLVER_ASSERT(allocated());
			if (!bufferHasSpace(bufferNumber, size) ) {
				buffers[bufferNumber]->resize(size, false);
			}	
		}
		
		/**
		 * @param numberOfBuffers the number of buffers to allocate
		 */
//		void allocate(const int numberOfBuffers)
//		{
//			if (allocated()) {
//				clear();
//			}
//			
//			this->numberOfBuffers = numberOfBuffers;
//			this->minSize = 0;
//			
//			buffers = new Buffer< T > * [numberOfBuffers];
//			
//			for ( int i = 0; i < numberOfBuffers; ++i ) {
//				buffers[i] = NULL;
//			}
//		}
		
		/**
		 * @param numberOfBuffers the number of buffers to allocate
		 * @param size the size of the buffers
		 */
		void allocate(const int numberOfBuffers, const int size)
		{
			if (allocated() && !needsResizing(numberOfBuffers, size)) {
				return;
			}
			
			if (allocated()) {
				clear();
			}
			
			this->numberOfBuffers = numberOfBuffers;
			this->minSize = size;
			
			buffers = new Buffer< T > *[numberOfBuffers];
			
			for ( int i = 0; i < numberOfBuffers; ++i ) {
				buffers[i] = new Buffer< T >(minSize); 
			}
		}
		
		~BufferSpace()
		{
			clear();
			
		}
		
		
		/**
		 * clears and frees all the allocated space
		 */
		inline
		void clear()
		{
			if(!allocated()) return;
			
			for ( int i = 0; i < numberOfBuffers; ++i ) {
				if ( buffers[i] != NULL) {
					delete buffers[i];
				}
			}
			
			delete[] buffers;
			reset();
		}
		
		/**
		 * @param b the buffer number
		 * @return the reference to the b(th) buffer
		 */
		inline
		Buffer< T > & operator[](const int b)
		{
			NSE_SOLVER_ASSERT( b < numberOfBuffers && b >= 0 );
			NSE_SOLVER_ASSERT( buffers != NULL );
			NSE_SOLVER_ASSERT( buffers[b] != NULL );
			
			return *buffers[b];
		}

		
		
	};
	
	
}

#endif
